package jdkclass.file;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 实时监控文件的变化
 *
 * @author zhibo
 * @date 2019-07-30 20:37
 */
public class FileWatchedService {

    private WatchService watchService;

    private FileWatchedListener listener;


    private final ExecutorService executorService;

    /**
     * @param path 要监听的目录，注意该 Path 只能是目录，否则会报错 java.nio.file.NotDirectoryException
     * @param listener 自定义的 listener，用来处理监听到的创建、修改、删除事件
     */
    public FileWatchedService(Path path, FileWatchedListener listener) throws IOException {
        watchService = FileSystems.getDefault().newWatchService();
        path.register(watchService,
            /// 监听文件创建事件
            StandardWatchEventKinds.ENTRY_CREATE,
            /// 监听文件删除事件
            StandardWatchEventKinds.ENTRY_DELETE,
            /// 监听文件修改事件
            StandardWatchEventKinds.ENTRY_MODIFY);

//            path.register(watchService,
//                    new WatchEvent.Kind[]{
//                            StandardWatchEventKinds.ENTRY_MODIFY,
//                            StandardWatchEventKinds.ENTRY_CREATE,
//                            StandardWatchEventKinds.ENTRY_DELETE
//                    },
//                    SensitivityWatchEventModifier.HIGH);

        this.listener = listener;
        this.executorService = Executors.newSingleThreadExecutor();
    }

    private void watch() throws InterruptedException {
        executorService.submit(() -> {
            WatchKey watchKey = watchService.take();
            try {
                while (true) {
                    List<WatchEvent<?>> watchEventList = watchKey.pollEvents();
                    for (WatchEvent<?> watchEvent : watchEventList) {
                        WatchEvent.Kind kind = watchEvent.kind();

                        WatchEvent<Path> curEvent = (WatchEvent<Path>) watchEvent;
                        if (kind == StandardWatchEventKinds.OVERFLOW) {
                            listener.onOverflowed(curEvent);
                            continue;
                        } else if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
                            listener.onCreated(curEvent);
                            continue;
                        } else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
                            listener.onModified(curEvent);
                            continue;
                        } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
                            listener.onDeleted(curEvent);
                            continue;
                        }
                    }
                }
            } finally {
                /**
                 * WatchKey 有两个状态：
                 * {@link sun.nio.fs.AbstractWatchKey.State.READY ready} 就绪状态：表示可以监听事件
                 * {@link sun.nio.fs.AbstractWatchKey.State.SIGNALLED signalled} 有信息状态：表示已经监听到事件，不可以接续监听事件
                 * 每次处理完事件后，必须调用 reset 方法重置 watchKey 的状态为 ready，否则 watchKey 无法继续监听事件
                 */
                if (watchKey != null) {
                    watchKey.reset(); // 重置 WatchKey
                }
            }
        });
    }

    public static void main(String[] args) {
        try {
            Path path = Paths.get("D:\\");
            FileWatchedService fileWatchedService = new FileWatchedService(path, new FileWatchedAdapter());
            fileWatchedService.watch();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

